package mpc;

import java.io.IOException;
import java.io.PrintWriter;

import javax.swing.JOptionPane;

public class VirtualField {
	private StateAndDist[][] dist;
	//private int[][] state;
	private final int[] 
					dx = { 0, 1, 0, -1},
					dy = {-1, 0, 1,  0};
	
	private IQueue queue;
	private boolean settingStartAllowed = true, startChoosen = false, managingBricksAllowed = true, success = false;
					//settingFinishAllowed = true, finishChoosen = false;
	
	public static int MAZE_WIDTH = 10, MAZE_HEIGHT = 10;
	
	public static final int START = 0, FINISH = -7, INF = 1000, BRICK = -1, IN_QUEUE = 2, FIRST_IN_QUEUE = 3, WORKED = 4, IN_PATH_TO_FINISH = 5;
	
	private int cstartx = 0, cstarty = 0,
			currentLastInQueue;
	
	VirtualField(int width, int height){
		/*
		dist = new StateAndDist[width][height];
		//state = new int[width][height];
		
		MAZE_WIDTH = width; MAZE_HEIGHT = height;
		
		currentLastInQueue = 0;
		
		for(int i = 0; i < width; i++)
			for(int j = 0; j < height; j++){
				dist[i][j] = new StateAndDist();
				//dist[i][j].dist = dist[i][j].state = INF;
			}
		queue = new PointerQueue();
		*/
		
		reset(width, height);
	}
	
	public void setInFile(PrintWriter out){
		out.println(MAZE_WIDTH + " " + MAZE_HEIGHT);
		for(int i = 0; i < dist.length; i++){
			for(int j = 0; j < dist[0].length; j++){
				if(dist[i][j].dist == BRICK){
					out.print(BRICK + " ");
				} else if(dist[i][j].dist == START){
					out.print(START + " ");
				} else if(dist[i][j].dist == FINISH){
					out.print(FINISH + " ");
				} else {
					out.print(INF + " ");
				}
			}
			out.println();
		}
		
		out.close();
	}
	
	public void loadFromFile(Reader in) throws IOException{
		
		settingStartAllowed = true; startChoosen = false; managingBricksAllowed = true; success = false;
		currentLastInQueue = 0; //settingFinishAllowed = true; finishChoosen = false;
		
		MAZE_WIDTH = in.nextInt(); MAZE_HEIGHT = in.nextInt();
		
		dist = new StateAndDist[MAZE_WIDTH][MAZE_HEIGHT];
		//state = new int[width][height];
		
		
		
		for(int i = 0; i < MAZE_WIDTH; i++)
			for(int j = 0; j < MAZE_HEIGHT; j++){
				dist[i][j] = new StateAndDist();
				dist[i][j].dist = dist[i][j].state = in.nextInt();
				if(dist[i][j].state == START) {
					startChoosen = true;
					cstartx = i;
					cstarty = j;
				}
				/*
				if(dist[i][j].state == FINISH){
					finishChoosen = true;
				}*/
			}
		queue = new PointerQueue();
		
		GUIClass.frame.setSize(MAZE_WIDTH * TableDrawer.SQ_SIZE + GUIClass.dWidth, MAZE_HEIGHT * TableDrawer.SQ_SIZE + GUIClass.dHeight);
	}
	
	public void restart(){
		for(int i = 0; i < MAZE_WIDTH; i++)
			for(int j = 0; j < MAZE_HEIGHT; j++){
				if((dist[i][j].state != BRICK) && (dist[i][j].state != START) && (dist[i][j].state != FINISH))
				dist[i][j] = new StateAndDist();
			}
		queue = new PointerQueue();
		
		settingStartAllowed = true; managingBricksAllowed = true; success = false;
		currentLastInQueue = 0; //settingFinishAllowed = true;
	}
	
	public void reset(int width, int height){
		dist = new StateAndDist[width][height];
		//state = new int[width][height];
		
		MAZE_WIDTH = width; MAZE_HEIGHT = height;
		
		for(int i = 0; i < width; i++)
			for(int j = 0; j < height; j++){
				dist[i][j] = new StateAndDist();
				//dist[i][j].dist = dist[i][j].state = INF;
			}
		queue = new PointerQueue();
		
		settingStartAllowed = true; startChoosen = false; managingBricksAllowed = true; success = false;
		currentLastInQueue = 0; //settingFinishAllowed = true; finishChoosen = false;
		
	}
	
	public void setStart(int x, int y){
		if(!settingStartAllowed) return;
		if(startChoosen)
			dist[cstartx][cstarty].dist = dist[cstartx][cstarty].state = INF;
		if(!isPossiblePosition(x, y) || !settingStartAllowed){return;}
		startChoosen = true;
		cstartx = x; cstarty = y;
		dist[x][y].dist = dist[x][y].state = START;
		//queue.add(new XYPair(x, y));
	}
	
	public void findPathTo(int x, int y){
		if((dist[x][y].dist == INF) || (dist[x][y].state == BRICK)){
			/*
			JOptionPane opane = new JOptionPane();
			opane.showMessageDialog(GUIClass.frame, "Path couldn't be found");
			*/
			(new JOptionPane()).showMessageDialog(GUIClass.frame, "Пути к указанной клетке нет!", "Невыполнимая задача", JOptionPane.ERROR_MESSAGE);
		} else {
			for(int i = 0; i < dist.length; i++)
				for(int j = 0; j < dist[0].length; j++)
					dist[i][j].inPath = false;
			
			
			System.out.println("Trying to find path");
			findPathTo(x, y, dist[x][y].dist);
		}
	}
	
	public boolean findPathTo(int x, int y, int Dist){
		if(!(isPossiblePosition(x, y) && (dist[x][y].state != START) && (dist[x][y].dist  == Dist))) return false;
		{
			//dist[x][y].state = IN_PATH_TO_FINISH;
			dist[x][y].inPath = true;
			/*
a:			for(int i = -1; i < 2; i++){
				for(int j = -1; j < 2; j++){
					boolean b = findPathTo(x + i, y + j, Dist - 1);
					if (b) break a;
				}
			}
			*/
a:			for(int i = 0; i < dx.length; i++){
				if(findPathTo(x + dx[i], y + dy[i], Dist - 1)) break a;
			}
		}
		return true;
	}
	
	public void setBrick(int x, int y){
		if(!managingBricksAllowed) return;
		
		if(!isPossibleForBrick(x, y)){return;}
		if(dist[x][y].state == BRICK) dist[x][y].dist = dist[x][y].state = INF;
		else dist[x][y].dist = dist[x][y].state = BRICK;
	}
	
	public boolean doNextStep(){
		if(!startChoosen) return false;
		
		if(settingStartAllowed) {
			queue.add(new XYPair(cstartx, cstarty));
			dist[cstartx][cstarty].state = IN_QUEUE;
			dist[cstartx][cstarty].numberInQueue = ++currentLastInQueue;
		}
		
		settingStartAllowed = managingBricksAllowed = false;
		
		if(queue.isEmpty()) return false;
		
		XYPair curr = queue.delete();
		dist[curr.x][curr.y].numberInQueue = -1;
		
		int cx, cy;
		/*
		for(int dx = -1; dx < 2; dx++){
			for(int dy = -1; dy < 2; dy++){
				cx = curr.x + dx; cy = curr.y + dy;
				if(isPossiblePosition(cx, cy) && dist[cx][cy].dist > dist[curr.x][curr.y].dist + 1){
					dist[cx][cy].dist = dist[curr.x][curr.y].dist + 1;
					queue.add(new XYPair(cx, cy));
					dist[cx][cy].state = IN_QUEUE;
					dist[cx][cy].numberInQueue = ++currentLastInQueue;
					//success = true;
				}
			}
		}
		*/
		
		for(int i = 0; i < dx.length; i++){
			cx = curr.x + dx[i]; cy = curr.y + dy[i];
			if(isPossiblePosition(cx, cy) && dist[cx][cy].dist > dist[curr.x][curr.y].dist + 1){
				dist[cx][cy].dist = dist[curr.x][curr.y].dist + 1;
				queue.add(new XYPair(cx, cy));
				dist[cx][cy].state = IN_QUEUE;
				dist[cx][cy].numberInQueue = ++currentLastInQueue;
				//success = true;
			}
		}
		if(!queue.isEmpty())
		dist[queue.first().x][queue.first().y].state = FIRST_IN_QUEUE;
		
			dist[curr.x][curr.y].state = dist[curr.x][curr.y].dist != START ? WORKED : START;
		
		return true;
	}
	
	public int getState(int x, int y){
		return dist[x][y].state;
	}
	
	public int getDist(int x, int y){
		return dist[x][y].dist;
	}
	
	public int numberInQueue(int x, int y){
		return dist[x][y].numberInQueue;
	}
	
	public boolean isInPath(int x, int y){
		return dist[x][y].inPath;
	}
	
	public boolean isStartChoosen(){
		return startChoosen;
	}
	
	private boolean isPossiblePosition(int x, int y){
		return (x >= 0) && (x < dist.length) && (y >= 0) && (y < dist[0].length) && (dist[x][y].dist != BRICK);
	}
	
	private boolean isPossibleForBrick(int x, int y){
		return (x >= 0) && (x < dist.length) && (y >= 0) && (y < dist[0].length) && (dist[x][y].dist == INF || dist[x][y].dist == BRICK);
	}
}

class StateAndDist{
	int dist = VirtualField.INF,
			state = VirtualField.INF,
			numberInQueue = -1;
	boolean inPath = false;
}
